----------------------------------------------------------------------------------
-- Company: 			StrathSat-R
-- Engineer: 			Thomas Parry
-- 
-- Create Date:    	23:16:09 08/02/2012 
-- Design Name: 		Data transmitter
-- Module Name:    	data_tx - Behavioral 
-- Project Name: 		FPGA data storage
-- Target Devices: 	Spartan 6
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity data_tx is
	generic
	(
		DATA_LENGTH		:	integer := 4096;
		INPUT_WIDTH		:	integer := 8;
		CRC_LENGTH		:	integer := 16
	);
   port 
	( 
		Clk 				: in  std_logic;
      Strobe 			: in  std_logic;
      Data 				: in  std_logic_vector(INPUT_WIDTH-1 downto 0);
      Output 			: out std_logic;
      Done 				: out std_logic;
		Data_Req			: out std_logic
	);
end data_tx;

architecture behavioral of data_tx is

	---- constants
	constant MAIN_LENGTH	:	integer := DATA_LENGTH;
	constant START_BITS	:	std_logic_vector(1 downto 0) := 	"01";
	constant END_BIT		:	std_logic := '1';

	---- signals
	-- counter variables
	signal count 			:	integer range 0 to MAIN_LENGTH+CRC_LENGTH-1;
	signal input_count	:	integer range 0 to INPUT_WIDTH;
	signal delay			:	integer range 0 to 3;
	
	-- shift register signals
	signal data_shift		: 	std_logic_vector(INPUT_WIDTH-1 downto 0);
	signal crc_shift		: 	std_logic_vector(CRC_LENGTH-1 downto 0);
	
	-- state flags
	signal crc_en			: 	std_logic;
	signal count_en		:	std_logic;
	
	-- main state machine type
	type state_type is
	(
		idle,
		pre,
		start,
		main,
		crc,
		finished
	);
	
	signal state : state_type;

begin

	---------------------------------------------------------------------------------------
	-- main state machine
	--		clocked process
	SM_CLKD : process(Clk)
	begin
	
		if rising_edge(Clk) then
			
			case state is
			
				when idle => 		if Strobe = '1' then
											state <= pre;
										else
											state <= idle;
										end if;
				
				---------------------------------------------------------------------
				
				when pre => 		if delay = 3 then
											delay <= 0;
											state <= start;
										else
											delay <= delay + 1;
											state <= pre;
										end if;
				
				---------------------------------------------------------------------
				
				when start =>		state <= main;
				
				---------------------------------------------------------------------
				
				when main =>		if count = MAIN_LENGTH-1 then
											state <= crc;
										else
											state <= main;
										end if;
				
				---------------------------------------------------------------------
				
				when crc =>			if count = MAIN_LENGTH+CRC_LENGTH-1 then
											state <= finished;
										else
											state <= crc;
										end if;
				
				---------------------------------------------------------------------
				
				when finished =>	state <= idle;
				
				---------------------------------------------------------------------
				
				when others =>		state <= idle;
				
			end case;
		end if;
		
	end process SM_CLKD;
	
	---------------------------------------------------------------------------------------
	-- main state machine
	--		combinational process
	SM_CMB : process(state)
	begin
		
		-- default values
		crc_en 		<= '0';
		count_en		<=	'0';
		Done			<= '0';
	
		case state is
			
				when idle => 
				---------------------------------------------------------------------
				when main =>		count_en	<= '1';
										crc_en	<=	'1';
				---------------------------------------------------------------------
				when crc =>			count_en	<= '1';
				---------------------------------------------------------------------
				when finished =>	Done 		<= '1';
				---------------------------------------------------------------------
				when others =>
				
			end case;
			
	end process SM_CMB;
	
	---------------------------------------------------------------------------------------
	-- Counting process
	
	COUNT_PROC	: process (Clk)
	begin
		
		if rising_edge(Clk) then
			
			if count_en = '0' then
				count <= 0;
			else
				count <= count + 1;
			end if;
			
		end if;
	end process COUNT_PROC;
	
	---------------------------------------------------------------------------------------
	-- Command shift register 
	
	SHIFT : process(Clk)
	begin
	
		if rising_edge(Clk) then
			
			if count_en = '1' then
				-- overall count
				data_shift(INPUT_WIDTH-1 downto 1) <= data_shift(INPUT_WIDTH-2 downto 0);
				data_shift(0) <= '0';
				
				if state = main then
					-- input count
					if input_count = INPUT_WIDTH-1 then
						data_shift	<= Data;
						input_count <= 0;
						Data_Req <= '0';
					else
						-- increment the input counter
						input_count <= input_count + 1;
						
						-- request new data
						if input_count = INPUT_WIDTH-2 then
							Data_Req <= '1';
						else
							Data_Req <= '0';
						end if;
					end if;
				end if;
				
			else
				data_shift <= Data;
			end if;
			
		end if;
	end process SHIFT;
	
	---------------------------------------------------------------------------------------
	-- CRC calculator
	CRC16 : if CRC_LENGTH = 16 generate
	
		CRC_CALC : process(Clk)
		begin
	
			if rising_edge(Clk) then
			
				if crc_en = '0' then
					crc_shift <= crc_shift(14 downto 0) & '0';
				else		
					crc_shift(15 downto 13) <= crc_shift(14 downto 12);
					crc_shift(12) <= data_shift(INPUT_WIDTH-1) xor crc_shift(15) xor crc_shift(11);
					crc_shift(11 downto 6) <= crc_shift(10 downto 5);
					crc_shift(5) <= data_shift(INPUT_WIDTH-1) xor crc_shift(15) xor crc_shift(4);
					crc_shift(4 downto 1) <= crc_shift(3 downto 0); 
					crc_shift(0) <= data_shift(INPUT_WIDTH-1) xor crc_shift(15);
				end if;
			end if;
			
		end process CRC_CALC;
	end generate CRC16;
	
	---------------------------------------------------------------------------------------
	-- output MUX
	
	-- select output depending on state
	with state select
		Output <=	'Z'								when idle,
						'1'								when pre,
						'0'								when start,
						data_shift(INPUT_WIDTH-1)	when main,
						crc_shift(CRC_LENGTH-1)		when crc,
						END_BIT							when finished,
						'Z'								when others;

end behavioral;

